package org.iteasy.framework.base;

import com.iteasy.manage.exception.BizException;
import org.iteasy.framework.utils.BeanUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.ParameterizedType;
import java.util.*;
import java.util.function.Consumer;

public class BaseServiceImpl<R extends BaseRepository<T,Long>,T> implements BaseService<T> {

    protected Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private R repository;

    protected R getRepository() {
        return this.repository;
    }

    /**
     * 获取 entity class对象
     * @return
     */
    private Class<T> getEntityClass() {
        Class<T> clazz = (Class<T>)((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[1];
        return clazz;
    }

    @Override
    public List<T> findAll() {
        return repository.findAll();
    }

    @Override
    public T findById(Long id) {
        return repository.findById(id).orElse(null);
    }

    @Override
    public List<T> findByIds(List<Long> ids) {
        return repository.findAllById(ids);
    }

    @Override
    public List<T> find(T t) {
        return repository.findAll(Example.of(t));
    }

    @Override
    public List<T> findByExample(Example<T> example) {
        return repository.findAll(example);
    }

    @Override
    public long findCountByExample(Example<T> example) {
        return repository.count(example);
    }

    @Override
    public List<T> findByNameIn(String name, List<Object> values) {
        return repository.findAll((root, query, criteria) -> criteria.and(root.isNotNull(), criteria.and(root.get(name).in(values))));
    }

    @Override
    public List<T> findByName2Value(Map<String, Object> map) {
        try {
            T entity = this.getEntityClass().getDeclaredConstructor().newInstance();
            map.forEach((key,value) -> BeanUtil.invokSet(entity, key, value));
            return this.repository.findAll();
        } catch (Exception e) {
            logger.error("",e);
        }
        return Collections.emptyList();
    }


    @Override
    public Page<T> page(Pageable pageable) {
        return repository.findAll(pageable);
    }

    @Override
    public Page<T> page(Example<T> example, Pageable pageable) {
        return repository.findAll(example,pageable);
    }

    @Override
    public Page<T> pageAndSort(Example<T> example, Pageable pageable, Sort sort) {
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public T save(T t) {
        return repository.save(t);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public T saveOrThrow(T t) {
        T t1 = this.save(t);
        if (Objects.isNull(t1)) {
            throw BizException.fail();
        }
        return t1;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean batchSave(List<T> list) {
        List<T> ts = repository.saveAll(list);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean batchSaveOrThrow(List<T> list) {
        List ts = repository.saveAll(list);
        if (ts.size() != list.size()) {
            throw BizException.fail();
        }
        return false;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean delete(Long id) {
        this.repository.deleteById(id);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean batchDelete(List<Long> ids) {
        ids.forEach(this.repository::deleteById);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchDelete(List<Long> ids, Consumer<Long> success, Consumer<Long> error) {
        for (Long id : ids) {
            try {
                this.repository.deleteById(id);
                success.accept(id);
                continue;
            } catch (Exception e) {
                logger.warn("[{}]删除失败",id);
            }
            error.accept(id);
        }
    }

    @Override
    public boolean existsForInsert(String field, Object value) {
        return Objects.nonNull(this.findByNameIn(field, Arrays.asList(value)));
    }

    @Override
    public void existsForInsert(String field, Object value, String msg) {
        if (this.existsForInsert(field, value)) {
            throw BizException.fail(msg);
        }
    }

    @Override
    public boolean existsForInsert(Map<String, Object> fieldMap) {
        List<T> entitis = this.findByName2Value(fieldMap);
        return !CollectionUtils.isEmpty(entitis);
    }

    @Override
    public void existsForInsert(Map<String, Object> fieldMap, String msg) {
        if (this.existsForInsert(fieldMap)) {
            throw BizException.fail(msg);
        }
    }

    @Override
    public boolean existsForUpdate(Long id, String field, Object value) {
        if (Objects.isNull(id)) {
            throw new RuntimeException("id can not be null.");
        }
        List<T> entitis = this.findByNameIn(field, Arrays.asList(value));
        if (CollectionUtils.isEmpty(entitis)) {
            return false;
        }
        for (T entity : entitis) {
            if (id.equals(BeanUtil.invokGet(entity,"id"))) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void existsForUpdate(Long id, String field, Object value, String msg) {
        if (existsForUpdate(id, field ,value)) {
            throw BizException.fail(msg);
        }
    }

    @Override
    public boolean existsForUpdate(Long id, Map<String, Object> fieldMap) {
        if (Objects.isNull(id)) {
            throw new RuntimeException("id can not be null.");
        }
        List<T> entitis = this.findByName2Value(fieldMap);
        if (CollectionUtils.isEmpty(entitis)) {
            return false;
        }
        for (T entity : entitis) {
            if (id.equals(BeanUtil.invokGet(entity,"id"))) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void existsForUpdate(Long id, Map<String, Object> fieldMap, String msg) {
        if (existsForUpdate(id, fieldMap)) {
            throw BizException.fail(msg);
        }
    }
}
